#pragma once
#include "ShaderParameter.h"
#include <list>
struct IDirect3DVertexDeclaration9;
struct IDirect3DVertexBuffer9;
struct _D3DVERTEXELEMENT9;
typedef struct _D3DVERTEXELEMENT9 D3DVERTEXELEMENT9;
struct ShaderDefineMap;
class VertexData;
class Shader;

//
// VertexFactoryParam
//
class VertexFactoryParam
{
public:
	virtual ~VertexFactoryParam() { }
	virtual void Bind(Shader* vertexShader) = 0;
	virtual void SetRenderable(RenderView* view, Renderable* renderable) = 0;
};

//
// VertexFactoryType
//
class VertexFactoryType
{
public:
	typedef bool (*ShouldCacheType)(const class Material*);
	typedef void (*DefineType)(ShaderDefineMap* defineMap);
	typedef VertexFactoryParam* (*CreateParam)();
	typedef std::list<const VertexFactoryType*> VertexFactoryTypeList;
	static VertexFactoryTypeList& getVertexFactoryTypeList() 
	{
		static VertexFactoryTypeList msVertexFactoryTypeList;
		return msVertexFactoryTypeList; 
	}

public:
	VertexFactoryType(const std::string& name, ShouldCacheType shouldCache, DefineType define, CreateParam create)
		: Name(name), ShouldCache(shouldCache), Define(define), ParamCreator(create)
	{
		getVertexFactoryTypeList().push_back(this);
	}

	const std::string Name;
	ShouldCacheType ShouldCache;
	DefineType Define;
	CreateParam ParamCreator;
};


//
// VertexFactory
//
class VertexFactory
{
public:

public:
	VertexFactory(const VertexData* vertexData);
	virtual ~VertexFactory();

	virtual const VertexFactoryType* GetType() const = 0;

	IDirect3DVertexDeclaration9* getDeclaration() { return mDeclaration; }
	IDirect3DVertexDeclaration9* getDeclaration() const { return mDeclaration; }

	IDirect3DVertexDeclaration9* getPositionDeclaration() { return mPositionDeclaration; }
	IDirect3DVertexDeclaration9* getPositionDeclaration() const { return mPositionDeclaration; }
	
	void Set(bool positionOnly = false) const;

	virtual void Init() {}

protected:
	void InitDeclaration(D3DVERTEXELEMENT9*);
	void InitPositionDeclaration(D3DVERTEXELEMENT9*);
	
private:
	const VertexData* mVertexData;
	IDirect3DVertexDeclaration9* mDeclaration;
	IDirect3DVertexDeclaration9* mPositionDeclaration;
};

//
// LocalVertexFactory
//
class LocalVertexFactory : public VertexFactory
{
public:
	class LocalVertexFactoryParam : public VertexFactoryParam
	{
	public:
		virtual void Bind(Shader* vertexShader);
		virtual void SetRenderable(RenderView* view, Renderable* renderable);
		
		ShaderParameter mWorldViewProjMatrixParam;
	};

public:
	static const VertexFactoryType StaticType;
	virtual const VertexFactoryType* GetType() const { return &StaticType; }
	
	static bool ShouldCache(const class Material*) { return true; }
	static void Define(ShaderDefineMap* defineMap) {}
	static VertexFactoryParam* CreateParam() { return new LocalVertexFactoryParam(); }

public:
	LocalVertexFactory(const VertexData* vertexData) : VertexFactory(vertexData) {}

	virtual void Init();
};

//
// GPUSkinVertexFactory
//
class GPUSkinVertexFactory : public VertexFactory
{
public:
	enum { MAX_BONES = 75 };

	class GPUSkinVertexFactoryParam : public VertexFactoryParam
	{
	public:
		virtual void Bind(Shader* vertexShader);
		virtual void SetRenderable(RenderView* view, Renderable* renderable);

		ShaderParameter mWorldViewProjMatrixParam;
		ShaderParameter mBoneMatricesParam;
	};

public:
	static const VertexFactoryType StaticType;
	virtual const VertexFactoryType* GetType() const { return &StaticType; }
	
	static bool ShouldCache(const class Material*);
	static void Define(ShaderDefineMap* defineMap);
	static VertexFactoryParam* CreateParam() { return new GPUSkinVertexFactoryParam(); }

public:
	GPUSkinVertexFactory(const VertexData* vertexData) : VertexFactory(vertexData) {}

	void Init();
};
